#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <ctype.h>
#include <dirent.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>

#include "net.h"
#include "func.h"
#include "private_common.h"


struct netdata
{
	char    name[32];
	uint8_t carrier;
	uint8_t operstate;
	int32_t speed;

	struct {
		uint64_t bytes;
		uint64_t packets;
		uint64_t dropped;
		uint64_t errors;
	} rx, tx;
};

#define FILE_NUM 11
static char *file_list[FILE_NUM] = {
	"carrier",
	"operstate",
	"speed",
	"statistics/rx_bytes",
	"statistics/rx_packets",
	"statistics/rx_dropped",
	"statistics/rx_errors",
	"statistics/tx_bytes",
	"statistics/tx_packets",
	"statistics/tx_dropped",
	"statistics/tx_errors"
};
static int file_index[FILE_NUM] = {0};

static struct netdata* net_data = NULL;
static int   net_nums = 0;

static void parse_param(void* ud, void *up, char* st)
{
	struct netdata* nd = (struct netdata*)ud;
	int* _index = (int*)up;
	if (!nd || !_index) return;
	if (!st) return;
	char* p = st;

	int index = *_index;
	switch (index) {
		case 0:
			nd->carrier = strtod(p, &p);
			break;
		case 1:
			if (startsWith(p, "up")) {
				nd->operstate = 1;
			} else {
				nd->operstate = 0;
			}
			break;
		case 2:
			nd->speed = strtod(p, &p);
			break;
		case 3:
			nd->rx.bytes = strtod(p, &p);
			break;
		case 4:
			nd->rx.packets = strtod(p, &p);
			break;
		case 5:
			nd->rx.dropped = strtod(p, &p);
			break;
		case 6:
			nd->rx.errors = strtod(p, &p);
			break;
		case 7:
			nd->tx.bytes = strtod(p, &p);
			break;
		case 8:
			nd->tx.packets = strtod(p, &p);
			break;
		case 9:
			nd->tx.dropped = strtod(p, &p);
			break;
		case 10:
			nd->tx.errors = strtod(p, &p);
			break;
	}
}

#define NET_DEVICE_PATH "/sys/class/net/"
static int  net_init(p_monitor_node *ml, const char* ignore)
{
	int k,j,i;
	DIR *dir;
	struct dirent *entry;
	
	for (i = 0; i < FILE_NUM; i++) {
		file_index[i] = i;
	}
	
	dir = opendir(NET_DEVICE_PATH);
	if (!dir) {
		perror("Failed to open thermal directory");
		return 0;
	}
	char devlist[200][32];
	char tmp[MAX_LEN_NAME];
	k = 0;
	while ((entry = readdir(dir)) != NULL) {
		if (startsWith(entry->d_name, "lo")) {
			continue;
		}
		if (ignore && strstr(ignore, entry->d_name)) {
			continue;
		}
		j = 0;
		for (i = 0; i < FILE_NUM; i++) {
			snprintf(tmp, MAX_LEN_NAME-1, "%s%s/%s", NET_DEVICE_PATH, entry->d_name, file_list[i]);
			if (exist_file(tmp) || (access(tmp, R_OK) != 0) || strlen(entry->d_name) > 31) {
				j = 1;
				break;
			}
		}
		if (j) continue;
		snprintf(devlist[k], 31, "%s", entry->d_name);
		k++;
		if (k >= 200) break;
	}

	int num = k*FILE_NUM;

	*ml = (p_monitor_node)malloc(sizeof(monitor_node) * num);
	net_data = (struct netdata*)malloc(sizeof(struct netdata)*k);
	for (i = 0;i < k; i++) {
		snprintf((net_data+i)->name, 31, "%s", devlist[i]);
		for (j = 0;j < FILE_NUM; j++) {
			snprintf(tmp, MAX_LEN_NAME-1, "%s%s/%s", NET_DEVICE_PATH, devlist[i], file_list[j]);
			init_monitor_info_interface(&((*ml+i*FILE_NUM+j)->oper), parse_param, (void*)(net_data+i), (void*)(&(file_index[j])));
			init_monitor_node(*ml+i*FILE_NUM+j, tmp, 1, 0);
		}
	}
	net_nums = k;
	return num;
}

static void net_update(p_monitor_node *ml)
{
	for (int i = 0;i < net_nums*FILE_NUM; i++) {
		update_monitor_node(*ml+i);
	}
}

static void net_clean()
{
	if (net_data)
		free(net_data);
	net_data = NULL;
	net_nums = 0;
}


static void net_to_json(cJSON* json)
{
	cJSON *node, *item;
	if (json == NULL) return;
	for (int i = 0;i < net_nums; i++) {
		item = cJSON_GetObjectItemCaseSensitive(json, (net_data+i)->name);
		if (item) {
			node = cJSON_GetObjectItem(json, (net_data+i)->name);
			cJSON_SetNumberValue(cJSON_GetObjectItem(node, "carrier"), (net_data+i)->carrier);
			cJSON_SetNumberValue(cJSON_GetObjectItem(node, "operstate"), (net_data+i)->operstate);
			cJSON_SetNumberValue(cJSON_GetObjectItem(node, "speed"), (net_data+i)->speed);
			cJSON_SetNumberValue(cJSON_GetObjectItem(node, "rx_bytes"), (net_data+i)->rx.bytes);
			cJSON_SetNumberValue(cJSON_GetObjectItem(node, "rx_packets"), (net_data+i)->rx.packets);
			cJSON_SetNumberValue(cJSON_GetObjectItem(node, "rx_dropped"), (net_data+i)->rx.dropped);
			cJSON_SetNumberValue(cJSON_GetObjectItem(node, "rx_errors"), (net_data+i)->rx.errors);
			cJSON_SetNumberValue(cJSON_GetObjectItem(node, "tx_bytes"), (net_data+i)->tx.bytes);
			cJSON_SetNumberValue(cJSON_GetObjectItem(node, "tx_packets"), (net_data+i)->tx.packets);
			cJSON_SetNumberValue(cJSON_GetObjectItem(node, "tx_dropped"), (net_data+i)->tx.dropped);
			cJSON_SetNumberValue(cJSON_GetObjectItem(node, "tx_errors"), (net_data+i)->tx.errors);
		} else {
			node = cJSON_CreateObject();
			cJSON_AddNumberToObject(node, "carrier", (net_data+i)->carrier);
			cJSON_AddNumberToObject(node, "operstate", (net_data+i)->operstate);
			cJSON_AddNumberToObject(node, "speed", (net_data+i)->speed);
			cJSON_AddNumberToObject(node, "rx_bytes", (net_data+i)->rx.bytes);
			cJSON_AddNumberToObject(node, "rx_packets", (net_data+i)->rx.packets);
			cJSON_AddNumberToObject(node, "rx_dropped", (net_data+i)->rx.dropped);
			cJSON_AddNumberToObject(node, "rx_errors", (net_data+i)->rx.errors);
			cJSON_AddNumberToObject(node, "tx_bytes", (net_data+i)->tx.bytes);
			cJSON_AddNumberToObject(node, "tx_packets", (net_data+i)->tx.packets);
			cJSON_AddNumberToObject(node, "tx_dropped", (net_data+i)->tx.dropped);
			cJSON_AddNumberToObject(node, "tx_errors", (net_data+i)->tx.errors);
			cJSON_AddItemToObject(json, (net_data+i)->name, node);
		}
	}
}

static uint32_t net_to_text(char* buffer, uint32_t size)
{
        char line[11][64];
        uint32_t k1 = 0, k2 = size;
        int r;
        for (int i = 0;i < net_nums; i++) {
                sprintf(line[0],  "%s,carrier,%d",     (net_data+i)->name, (net_data+i)->carrier);
                sprintf(line[1],  "%s,operstate,%d",   (net_data+i)->name, (net_data+i)->operstate);
                sprintf(line[2],  "%s,speed,%d",       (net_data+i)->name, (net_data+i)->speed);
                sprintf(line[3],  "%s,rx_bytes,%ld",   (net_data+i)->name, (net_data+i)->rx.bytes);
                sprintf(line[4],  "%s,rx_packets,%ld", (net_data+i)->name, (net_data+i)->rx.packets);
                sprintf(line[5],  "%s,rx_dropped,%ld", (net_data+i)->name, (net_data+i)->rx.dropped);
                sprintf(line[6],  "%s,rx_errors,%ld",  (net_data+i)->name, (net_data+i)->rx.errors);
                sprintf(line[7],  "%s,tx_bytes,%ld",   (net_data+i)->name, (net_data+i)->tx.bytes);
                sprintf(line[8],  "%s,tx_packets,%ld", (net_data+i)->name, (net_data+i)->tx.packets);
                sprintf(line[9],  "%s,tx_dropped,%ld", (net_data+i)->name, (net_data+i)->tx.dropped);
                sprintf(line[10], "%s,tx_errors,%ld",  (net_data+i)->name, (net_data+i)->tx.errors);
		for (int j = 0; j < 11; j++) {
			r = snprintf(buffer+k1, k2, "%s\n", line[j]);
			if (r <= 0) return k1;
			k1 += r;
			if (k1 >= size) return k1;
			k2 -= r;
		}
        }
        return k1;
}

void net_task_init(ptask t, char* _ignore, uint16_t _uf)
{
        t->init    = net_init;
        t->update  = net_update;
        t->to_json = net_to_json;
        t->clean   = net_clean;
        t->json    = NULL;
        t->ignore  = _ignore;
        t->uf      = _uf;
        t->cu      = 0;
	t->to_text = net_to_text;
}

